import yfinance as yf
import pandas as pd
from datetime import datetime, timedelta
from typing import Optional, Dict, List, Any
import time
import requests
import re

from .utils.config import get_settings
from .utils.logger import get_logger
from .utils.validators import DataValidator
from .ashare_fetcher import AShareFetcher, AShareDataFetchError
from .data_source_manager import DataSourceManager


class DataFetchError(Exception):
    """数据获取异常"""
    pass


class DataFetcher:
    """数据获取核心引擎"""

    def __init__(self):
        self.settings = get_settings()
        self.logger = get_logger("DataFetcher")
        self.fetch_config = self.settings["data_fetch"]
        self.session = requests.Session()
        self.ashare_fetcher = AShareFetcher()
        self.data_source_manager = DataSourceManager(auto_check=False)
        
    def _apply_request_delay(self):
        """应用请求延迟"""
        delay = self.fetch_config.get("request_delay", 1.0)
        time.sleep(delay)
        
    def _retry_operation(self, operation, *args, **kwargs):
        """重试机制"""
        max_retries = self.fetch_config.get("max_retries", 3)
        
        for attempt in range(max_retries):
            try:
                return operation(*args, **kwargs)
            except Exception as e:
                if attempt == max_retries - 1:
                    raise e
                    
                wait_time = 2 ** attempt  # 指数退避
                self.logger.warning(f"Attempt {attempt + 1} failed: {e}. Retrying in {wait_time}s...")
                time.sleep(wait_time)
                
    def validate_symbol(self, symbol: str) -> bool:
        """验证股票代码有效性"""
        try:
            # 使用验证器先检查格式
            if not DataValidator.validate_symbol(symbol):
                return False

            # A股股票使用专门的验证器
            if re.match(r'^\d{6}$', symbol):
                return self.ashare_fetcher.validate_symbol(symbol)

            # 其他股票使用yfinance验证
            ticker = yf.Ticker(symbol)
            info = ticker.info

            # 检查是否有基本信息
            if not info or len(info) < 5:
                return False

            # 检查是否有价格数据
            test_data = ticker.history(period="5d")
            return not test_data.empty

        except Exception as e:
            self.logger.debug(f"Symbol validation failed for {symbol}: {e}")
            return False
            
    def get_symbol_info(self, symbol: str) -> Dict[str, Any]:
        """获取股票基本信息"""
        try:
            # A股股票使用专门的获取器
            if re.match(r'^\d{6}$', symbol):
                return self.ashare_fetcher.get_symbol_info(symbol)

            # 其他股票使用yfinance
            ticker = yf.Ticker(symbol)
            info = ticker.info

            return {
                'symbol': symbol,
                'name': info.get('longName', 'Unknown'),
                'sector': info.get('sector', 'Unknown'),
                'industry': info.get('industry', 'Unknown'),
                'market': info.get('market', 'Unknown'),
                'currency': info.get('currency', 'USD'),
                'timezone': info.get('exchangeTimezoneName', 'UTC')
            }

        except Exception as e:
            self.logger.error(f"Failed to get symbol info for {symbol}: {e}")
            return {'symbol': symbol, 'name': 'Unknown'}
            
    def fetch_full_history(self, symbol: str, interval: str = '1d') -> pd.DataFrame:
        """
        获取股票完整历史数据

        Args:
            symbol: 股票代码 (如 'AAPL', '688228')
            interval: 数据频率 ('1d', '1h', '5m' 等)

        Returns:
            包含完整历史数据的DataFrame

        Raises:
            DataFetchError: 数据获取失败
        """
        try:
            self.logger.info(f"Fetching full history for {symbol} with interval {interval}")

            # A股股票使用专门的获取器
            if re.match(r'^\d{6}$', symbol):
                try:
                    data = self.ashare_fetcher.fetch_history_data(symbol, interval)
                    if not data.empty:
                        # 确保DataFrame格式一致：Date作为列而非索引
                        if 'Date' not in data.columns and data.index.name == 'Date':
                            data = data.reset_index()
                        self.logger.info(f"Fetched {len(data)} records for {symbol} ({interval}) using AShareFetcher")
                        return data
                except AShareDataFetchError as e:
                    self.logger.warning(f"AShareFetcher failed for {symbol}: {e}")

            # 如果A股获取失败或者不是A股，使用yfinance
            def _fetch():
                ticker = yf.Ticker(symbol)

                # 对于日线数据，获取所有历史数据
                if interval == '1d':
                    data = ticker.history(period="max", interval=interval, actions=True)
                else:
                    # 对于分钟/小时数据，yfinance有限制，只能获取较短期间的数据
                    period_map = {
                        '1h': '730d',   # 2年
                        '30m': '60d',   # 2个月
                        '15m': '60d',   # 2个月
                        '5m': '60d'     # 2个月
                    }
                    period = period_map.get(interval, '1y')
                    data = ticker.history(period=period, interval=interval, actions=True)

                return data

            data = self._retry_operation(_fetch)
            self._apply_request_delay()

            if data.empty:
                raise DataFetchError(f"No data available for {symbol}")

            # 重置索引，将Date作为列
            data = data.reset_index()

            self.logger.info(f"Fetched {len(data)} records for {symbol} ({interval}) using yfinance")
            return data

        except Exception as e:
            error_msg = f"Failed to fetch full history for {symbol}: {e}"
            self.logger.error(error_msg)
            raise DataFetchError(error_msg)
            
    def fetch_incremental_update(self, symbol: str, last_date: str, interval: str = '1d') -> pd.DataFrame:
        """
        增量更新数据

        Args:
            symbol: 股票代码
            last_date: 本地数据的最后日期
            interval: 数据频率

        Returns:
            新增数据的DataFrame
        """
        try:
            self.logger.info(f"Fetching incremental update for {symbol} from {last_date}")

            # A股股票使用专门的获取器
            if re.match(r'^\d{6}$', symbol):
                try:
                    data = self.ashare_fetcher.fetch_incremental_update(symbol, last_date, interval)
                    if not data.empty:
                        self.logger.info(f"Fetched {len(data)} new records for {symbol} using AShareFetcher")
                        return data
                except AShareDataFetchError as e:
                    self.logger.warning(f"AShareFetcher incremental update failed for {symbol}: {e}")

            # 其他股票使用yfinance
            # 计算开始日期（最后日期的下一天）
            last_dt = datetime.strptime(last_date, '%Y-%m-%d')
            start_date = (last_dt + timedelta(days=1)).strftime('%Y-%m-%d')
            end_date = datetime.now().strftime('%Y-%m-%d')

            # 如果开始日期就是今天或更晚，没有新数据
            if start_date >= end_date:
                self.logger.info(f"No new data available for {symbol}")
                return pd.DataFrame()

            def _fetch():
                ticker = yf.Ticker(symbol)
                data = ticker.history(start=start_date, end=end_date, interval=interval, actions=True)
                return data

            data = self._retry_operation(_fetch)
            self._apply_request_delay()

            if data.empty:
                self.logger.info(f"No new data found for {symbol} since {last_date}")
                return pd.DataFrame()

            # 重置索引
            data = data.reset_index()

            self.logger.info(f"Fetched {len(data)} new records for {symbol} using yfinance")
            return data

        except Exception as e:
            error_msg = f"Failed to fetch incremental update for {symbol}: {e}"
            self.logger.error(error_msg)
            raise DataFetchError(error_msg)
            
    def fetch_batch_symbols(self, symbols: List[str], interval: str = '1d') -> Dict[str, pd.DataFrame]:
        """
        批量获取多个股票数据
        
        Args:
            symbols: 股票代码列表
            interval: 数据频率
            
        Returns:
            {symbol: dataframe} 字典
        """
        results = {}
        
        for i, symbol in enumerate(symbols):
            try:
                self.logger.info(f"Processing {symbol} ({i+1}/{len(symbols)})")
                
                # 获取完整历史数据
                data = self.fetch_full_history(symbol, interval)
                results[symbol] = data
                
                # 批量处理时添加额外延迟
                if i < len(symbols) - 1:  # 不是最后一个
                    time.sleep(0.5)
                    
            except Exception as e:
                self.logger.error(f"Failed to fetch data for {symbol}: {e}")
                results[symbol] = pd.DataFrame()  # 空数据框表示失败
                
        success_count = sum(1 for df in results.values() if not df.empty)
        self.logger.info(f"Batch fetch completed: {success_count}/{len(symbols)} successful")
        
        return results
        
    def fetch_recent_data(self, symbol: str, days: int = 30, interval: str = '1d') -> pd.DataFrame:
        """
        获取最近N天的数据
        
        Args:
            symbol: 股票代码
            days: 天数
            interval: 数据频率
            
        Returns:
            最近数据的DataFrame
        """
        try:
            self.logger.info(f"Fetching recent {days} days data for {symbol}")

            def _fetch():
                ticker = yf.Ticker(symbol)
                data = ticker.history(period=f"{days}d", interval=interval, actions=True)
                return data

            data = self._retry_operation(_fetch)
            self._apply_request_delay()

            if data.empty:
                self.logger.warning(f"No recent data found for {symbol}")
                return pd.DataFrame()

            # 重置索引
            data = data.reset_index()

            self.logger.info(f"Fetched {len(data)} recent records for {symbol}")
            return data

        except Exception as e:
            self.logger.error(f"Failed to fetch recent data for {symbol}: {e}")
            return pd.DataFrame()

    def fetch_with_smart_source_selection(self, symbol: str, interval: str = '1d',
                                        start_date: str = None, end_date: str = None) -> pd.DataFrame:
        """
        使用智能数据源选择获取数据

        Args:
            symbol: 股票代码
            interval: 数据频率
            start_date: 开始日期
            end_date: 结束日期

        Returns:
            数据DataFrame
        """
        try:
            # 获取最佳数据源列表
            best_sources = self.data_source_manager.get_best_sources_for_task('history')

            for source_name in best_sources:
                try:
                    self.logger.info(f"尝试使用数据源 {source_name} 获取 {symbol} 数据")

                    if start_date and end_date:
                        # 获取指定时间范围的数据
                        data, used_source = self.data_source_manager.fetch_data_with_fallback(
                            symbol, interval, start_date, end_date
                        )
                    else:
                        # 获取全部历史数据
                        data = self.fetch_full_history(symbol, interval)
                        used_source = source_name

                    if not data.empty:
                        self.logger.info(f"成功使用 {used_source} 获取 {symbol} 数据: {len(data)} 条记录")
                        return data

                except Exception as e:
                    self.logger.warning(f"数据源 {source_name} 获取 {symbol} 失败: {e}")
                    continue

            # 如果所有数据源都失败，尝试原有方法
            self.logger.warning(f"所有智能数据源都失败，尝试原有方法获取 {symbol}")
            return self.fetch_full_history(symbol, interval)

        except Exception as e:
            self.logger.error(f"智能数据源选择失败 {symbol}: {e}")
            return pd.DataFrame()

    def validate_and_clean_data(self, data: pd.DataFrame, symbol: str) -> pd.DataFrame:
        """
        验证和清理数据

        Args:
            data: 原始数据
            symbol: 股票代码

        Returns:
            清理后的数据
        """
        if data.empty:
            return data

        try:
            # 1. 检查必需列
            required_columns = ['Date', 'Open', 'High', 'Low', 'Close', 'Volume']
            missing_columns = [col for col in required_columns if col not in data.columns]
            if missing_columns:
                self.logger.warning(f"{symbol} 数据缺少列: {missing_columns}")
                return pd.DataFrame()

            # 2. 数据类型转换
            data['Date'] = pd.to_datetime(data['Date'])
            for col in ['Open', 'High', 'Low', 'Close', 'Volume']:
                data[col] = pd.to_numeric(data[col], errors='coerce')

            # 3. 删除无效数据
            data = data.dropna(subset=['Open', 'High', 'Low', 'Close'])

            # 4. 价格合理性检查
            price_columns = ['Open', 'High', 'Low', 'Close']
            for col in price_columns:
                # 删除负价格和异常高价格
                data = data[(data[col] > 0) & (data[col] < 10000)]

            # 5. 高低价格逻辑检查
            data = data[data['High'] >= data['Low']]
            data = data[(data['High'] >= data['Open']) & (data['High'] >= data['Close'])]
            data = data[(data['Low'] <= data['Open']) & (data['Low'] <= data['Close'])]

            # 6. 去重并排序
            data = data.drop_duplicates(subset=['Date']).sort_values('Date')

            # 7. 检查数据连续性（可选）
            if len(data) > 1:
                date_diff = data['Date'].diff().dt.days
                # 检查是否有异常的日期跳跃（超过30天）
                large_gaps = date_diff > 30
                if large_gaps.any():
                    self.logger.warning(f"{symbol} 数据存在较大日期间隔")

            self.logger.info(f"{symbol} 数据验证完成，保留 {len(data)} 条有效记录")
            return data

        except Exception as e:
            self.logger.error(f"{symbol} 数据验证失败: {e}")
            return pd.DataFrame()
            
        except Exception as e:
            error_msg = f"Failed to fetch recent data for {symbol}: {e}"
            self.logger.error(error_msg)
            raise DataFetchError(error_msg)